/*!
 Temelia - Performance tests core file; it loads the performance tests, depending on
 the user input.

 The performance tests are outsourced into separate source files: data_structures.cpp.
 In order to add a new test for a data structure you must:
 1) Add a new function to class TemeliaPerformance having the name pt_data_structure()
 2) Add it's id to TemeliaPerformance::__tests_name
 3) Add the pointer to function to __tests_function

 Another way is to add a new pair (id, behaviour) to
 std::map<std::string, void(*)(void)> string_to_test;

 Copyright (C) 2008 Ceata (http://cod.ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "include/performance.hpp"
#include "include/testutil.hpp"
#include <utility>

void test();

int main(int argc, char **argv)
{
	TemeliaPerformance::init();

	if (argc != 2)
	{
		std::cout << "Usage : ./temelia_performance $test_name$\n" << std::endl;
		return EXIT_FAILURE;
	}

	if (argc == 2)
	{
		std::cout << "Running the test for " << argv[1] << std::endl;
		TemeliaPerformance::run_test(argv[1]);
		return EXIT_SUCCESS;
	}

	return EXIT_FAILURE;
}

char TemeliaPerformance::__tests_name[TESTS_NUMBER][20] =
{ "algorithms", "avl_tree", "b_tree", "binary_search_tree", "binary_tree",
		"doubly_linked_list", "hash_set", "hash_map", "heap", "interval_tree",
		"linked_list", "graph", "queue", "red_black_tree", "scapegoat_tree",
		"skip_list", "sort", "splay_tree", "stack", "treap_tree",
		"tree_algorithms", "tree", "trie_tree", "vector" };

void (*TemeliaPerformance::__tests_function[TESTS_NUMBER])(void) =
{
	pt_algorithms, pt_avl, pt_b_tree, pt_binary_search_tree, pt_binary_tree,
	pt_doubly_linked_list, pt_hash_set, pt_hash_map, pt_heap,
	pt_interval_tree, pt_linked_list, pt_graph,
	pt_queue, pt_red_black_tree, pt_scapegoat_tree, pt_skip_list, pt_sort,
	pt_splay_tree, pt_stack, pt_treap_tree,
	pt_tree_algorithms, pt_tree, pt_trie, pt_vector
};

std::map<std::string, void(*)(void)> TemeliaPerformance::string_to_test;
std::ofstream *TemeliaPerformance::fout;

void TemeliaPerformance::init()
{
	// Add the pairs (id, behaviour) into the std::map
	for (int i = 0; i < TESTS_NUMBER; i++)
		string_to_test.insert(std::make_pair(__tests_name[i],
				__tests_function[i]));
	fout = new std::ofstream("performance_log.txt", std::ios::out
			| std::ios::app);
	init_collection();
}

void TemeliaPerformance::finish()
{
	fout->close();
	delete fout;
	finish_collection();
}

void TemeliaPerformance::run_test(std::string test)
{
	std::map<std::string, void(*)(void)>::const_iterator it =
			string_to_test.find(test);

	// Call the function associated with 'test' only if
	// 'test' is a valid test name
	if (it != string_to_test.end())
		it->second();
}

void TemeliaPerformance::my_log(std::string data_structure,
		std::string function_name, std::vector<int> &input_size, std::vector<
				double> &time, std::string library, bool start, bool finish)
{
	if (time.size() != input_size.size())
	{
		printf("Invalid input for logging ....\n");
		exit(1);
	}

	if (start)
		(*fout) << "<data_structure name = \"" << data_structure << "\">"
				<< std::endl;

	(*fout) << "   <function name = \"" << function_name << "\" lib = \""
			<< library + "\"" << std::endl;

	unsigned int size = time.size();
	for (unsigned int i = 0; i < size; i++)
		(*fout) << "      <test input_size = \"" << input_size[i]
				<< "\" time = \"" << time[i] << "\"/>" << std::endl;
	(*fout) << "   </function>" << std::endl;

	if (finish)
		(*fout) << "</data_structure>" << std::endl << std::endl;
}

int int_compare(void *x, void *y, void *context)
{
	if (x == NULL && y != NULL)
		return -1;
	else if (x != NULL && y == NULL)
		return 1;
	return *(int *) x - *(int *) y;
}

void int_delete(void *x, void *context)
{
	free(x);
}

int **int_array_new(int size)
{
	int **data = (int **) malloc(size * sizeof(int *));

	for (int i = 0; i < size; i++)
	{
		data[i] = (int *) malloc(sizeof(int));
		data[i][0] = rand();
	}

	return data;
}

void int_array_delete(int **data, int size)
{
	for (int i = 0; i < size; i++)
		free(data[i]);
	free(data);
}

bool stl_int_compare(int *x, int *y)
{
	return false;
}

void init_collection()
{
	for (int i = 0; i < MAX_TEST_SIZE; i++)
		default_collection[i] = int_new(rand());
}

void finish_collection()
{
	for (int i = 0; i < MAX_TEST_SIZE; i++)
		int_delete(default_collection[i], NULL);
}

void exit_with_error(std::string error)
{
	std::cerr << error << std::endl;
	std::cerr.flush();
	exit(1);
}

void int_handler(void *x, void *context)
{
	printf("%d ", *(int *) x);
}

void int_level_handler(void *key, int level, void *fout)
{
	int i;
	for (i = 0; i < level; i++)
		printf("  ");
	if (key == NULL)
		printf("\nNULL\n");
	else
		printf("%d\n", *(int *) key);
}

int *int_new(int default_value)
{
	int *x;

	x = (int *) malloc(sizeof(int));
	*x = default_value;

	return x;
}

void *my_key_at(void *data, int index)
{
	// Basically it's data[index].
	return (void *) ((int **) data)[index];
}

void my_set_key_at(void *data, int index, void *key)
{
	((void **) data)[index] = key;
}
